package com.feng.study.demo.collectionLearn;

import com.feng.study.model.Product;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;

/**
 * map学习
 * <p>
 * 1.8 做了什么优化？
 * 线程安全怎么做的？
 * 不安全会导致哪些问题？
 * 如何解决？有没有线程安全的并发容器？
 * ConcurrentHashMap 是如何实现的？
 * ConcurrentHashMap并发度为啥好这么多？
 * 1.7、1.8 实现有何不同？为什么这么做？
 * CAS是啥？
 * ABA是啥？场景有哪些，怎么解决？
 * synchronized底层原理是啥？
 * synchronized锁升级策略
 * <p>
 * 快速失败（fail—fast）是啥，应用场景有哪些？安全失败（fail—safe）同问。
 * testFailSafe()
 */
public class MapDemo01 {
    public static void main(String[] args) {
//        HashMap<Integer, String> map = new HashMap<>();
//        //线程安全的map
//        //1、加排斥锁 mutex
//        Map<Integer, String> stringStringMap = Collections.synchronizedMap(map);
//        //2、Hashtable
//        Hashtable<Integer, String> hashtable = new Hashtable<>();
//        //3、ConcurrentHashMap 最常用的（为什么？做了哪些优化？）
//        ConcurrentHashMap<Integer, String> concurrentHashMap = new ConcurrentHashMap<>();
//        concurrentHashMap.put(1, "aaa");
//        concurrentHashMap.get(1);
//
//        testHash();

        //stream-toMap
//        testStream();

        // 测试空值null
//        testNull();

        testFailSafe();
    }

    /**
     * hashMap
     * hash冲突解决方案
     * hash计算
     */
    private static void testHash() {
        HashMap<Integer,String> map = new HashMap<>();
        // 默认桶长度为16
        // 所以下面key=1和key=17 hash值相同都等于1。即hash冲突
        // hashmap采用链式寻址法，解决hash冲突
        map.put(1,"aaa");
        map.put(17,"bbb");
        map.put(2,"bbb");
        System.out.println(map.get(17));

    }

    private static void testStream() {
        Product product = new Product();
        product.setProductId("001").setName("aaa");
        Product product1 = new Product();
        product1.setProductId("002").setName("bbb");
        List<Product> products = Lists.newArrayList(product, product1);
        Map<String, String> productMap = products.stream().collect(
                Collectors.toConcurrentMap(Product::getProductId, Product::getName, (v1, v2) -> v1));
        System.out.println(productMap);
        String ccc = productMap.merge("002", "bbb", (v1, v2) -> {
            if (v1.equals(v2)) {
                return "equals";
            } else {
                return v2;
            }
        });
        System.out.println(ccc);
        System.out.println(productMap);
    }

    private static void testNull() {
        System.out.println("=======测试空值null++++++++++++");
        // hashMap允许缓存空值
        // 只允许一个key为null
        // 缺陷：存在二义性，如下面例子
        //      但是可以通过containsKey()方法判断是否key存在
        //      ConcurrentHashMap则不能通过containsKey()方法判断是否key存在
        HashMap<String, String> map = new HashMap<>();
        map.put(null, null);
        map.put("aaa", null);
        System.out.println("null=" + map.get(null));
        System.out.println("aaa=" + map.get("aaa"));
        System.out.println("bbb=" + map.get("bbb"));

        System.out.println(map.containsKey("aaa"));
        System.out.println(map.containsKey("bbb"));

        //hashTable中key和value都不允许空值null
        Hashtable<String, String> hashtable = new Hashtable<>();
//        hashtable.put(null,"aaa");

        // ConcurrentHashMap中key和value都不允许空值null
        // if (key == null || value == null) throw new NullPointerException();
        ConcurrentHashMap<Integer,String > concurrentHashMap = new ConcurrentHashMap<>();
//        concurrentHashMap.put(null,null);
        concurrentHashMap.put(1,"aaa");
        concurrentHashMap.put(2,"aaa");
        concurrentHashMap.put(17,"aaa");
        concurrentHashMap.size();
        Iterator<Map.Entry<Integer, String>> iterator = concurrentHashMap.entrySet().iterator();
        while (iterator.hasNext()){
            Map.Entry<Integer, String> next = iterator.next();
            System.out.println(next.getKey());
            System.out.println(next.getValue());
        }
        System.out.println(concurrentHashMap.containsKey(17));

    }

    /**
     * Java集合的一种错误检查机制
     * 快速失败：fail-fast ArrayList、HashMap...
     * 安全失败：fail-safe  ConcurrentHashMap、Hashtable、CopyOnWriteArrayList...
     * <p>
     * 学习文章：https://blog.csdn.net/a745233700/article/details/83387688
     */
    public static void testFailSafe() {
        ConcurrentHashMap<Integer, String> concurrentHashMap = new ConcurrentHashMap<>();
        concurrentHashMap.put(1, "aaa");
        concurrentHashMap.put(2, "bbb");
        concurrentHashMap.put(3, "ccc");
        //fail-safe 安全失败机制，复制一份再遍历，存在读不到最新数据的情况
        Iterator<Integer> iterator = concurrentHashMap.keySet().iterator();
        while (iterator.hasNext()) {
            System.out.println(concurrentHashMap.get(iterator.next()));
            concurrentHashMap.put(0, "fff");
            // iterator 遍历时 弱一致性
            // 输出了ddd，但是没有输出新插入的“fff”
            concurrentHashMap.put(4, "ddd");
        }
    }
}
